# -*- coding: utf-8 -*-
"""
抖音APP自动化操作脚本
使用Appium实现自动化点击、页面跳转等功能
"""

from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import time
import yaml
import os


class Config:
    """配置管理类"""
    
    _instance = None
    _config = None
    
    def __new__(cls):
        """单例模式"""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        """初始化配置"""
        if self._config is None:
            self.load_config()
    
    def load_config(self, config_file="config.yaml"):
        """
        从 YAML 文件加载配置
        
        Args:
            config_file: 配置文件路径
        """
        try:
            if os.path.exists(config_file):
                with open(config_file, 'r', encoding='utf-8') as f:
                    self._config = yaml.safe_load(f)
                print(f"✓ 已从 {config_file} 读取配置")
            else:
                print(f"⚠️ 未找到配置文件 {config_file}，使用默认配置")
                self._config = self._get_default_config()
        except Exception as e:
            print(f"⚠️ 读取配置文件失败: {str(e)[:100]}，使用默认配置")
            self._config = self._get_default_config()
    
    def _get_default_config(self):
        """获取默认配置"""
        return {
            'private_message': {
                'content': '叮咚！一份专属感谢已送达～ 感谢你的关注！这对我来说是超棒的鼓励！希望我的内容能一直为你带来快乐或一点点启发。衷心祝愿你一切顺利，笑容常挂脸上！😊    --来自清华大学做AI智能体的文正听心奉上',
                'wait_after_send': 0.5
            },
            'device': {
                'name': 'Android',
                'platform_version': '12',
                'appium_server': 'http://localhost:4723/wd/hub'
            },
            'automation': {
                'show_touches': True,
                'wait_after_click': 0.5,
                'scroll_duration': 200,
                'page_load_wait': 1.5
            },
            'douyin': {
                'package': 'com.ss.android.ugc.aweme',
                'activity': 'com.ss.android.ugc.aweme.splash.SplashActivity'
            },
            'follow': {
                'max_no_button_count': 3,
                'auto_refresh_add_friend': True
            },
            'screenshot': {
                'enabled': True,
                'save_path': './'
            }
        }
    
    def get(self, key_path, default=None):
        """
        获取配置项
        
        Args:
            key_path: 配置路径，用点分隔，如 'private_message.content'
            default: 默认值
            
        Returns:
            配置值
        """
        keys = key_path.split('.')
        value = self._config
        
        for key in keys:
            if isinstance(value, dict) and key in value:
                value = value[key]
            else:
                return default
        
        return value
    
    def reload(self):
        """重新加载配置"""
        self._config = None
        self.load_config()


class DouyinAutomation:
    """抖音自动化操作类"""
    
    def __init__(self, device_name=None, platform_version=None):
        """
        初始化抖音自动化操作
        
        Args:
            device_name: 设备名称（如果为None，从配置文件读取）
            platform_version: Android版本（如果为None，从配置文件读取）
        """
        self.driver = None
        self.config = Config()
        
        # 从配置文件读取设备信息（如果没有提供参数）
        self.device_name = device_name or self.config.get('device.name', 'Android')
        self.platform_version = platform_version or self.config.get('device.platform_version', '12')
    
    def load_private_message_from_config(self):
        """
        从配置文件读取私信内容
        
        Returns:
            私信内容字符串
        """
        message = self.config.get('private_message.content')
        if message:
            print(f"✓ 已从配置文件读取私信内容")
            return message
        else:
            # 默认内容
            default_message = "叮咚！一份专属感谢已送达～ 感谢你的关注！这对我来说是超棒的鼓励！希望我的内容能一直为你带来快乐或一点点启发。衷心祝愿你一切顺利，笑容常挂脸上！😊    --来自清华大学做AI智能体的文正听心奉上"
            print(f"⚠️ 配置文件中未找到私信内容，使用默认内容")
            return default_message
        
    def enable_show_touches(self):
        """开启显示触摸操作（虚拟鼠标）"""
        try:
            import subprocess
            # 开启显示触摸点（增加超时时间到15秒）
            subprocess.run(['adb', 'shell', 'settings', 'put', 'system', 'show_touches', '1'], 
                          capture_output=True, timeout=15)
            # 开启指针位置（显示触摸轨迹）
            subprocess.run(['adb', 'shell', 'settings', 'put', 'system', 'pointer_location', '1'], 
                          capture_output=True, timeout=15)
            print("✓ 已开启虚拟鼠标显示")
        except Exception as e:
            print(f"⚠️ 开启虚拟鼠标失败（不影响主要功能）: {str(e)[:80]}")
    
    def disable_show_touches(self):
        """关闭显示触摸操作"""
        try:
            import subprocess
            subprocess.run(['adb', 'shell', 'settings', 'put', 'system', 'show_touches', '0'], 
                          capture_output=True, timeout=15)
            subprocess.run(['adb', 'shell', 'settings', 'put', 'system', 'pointer_location', '0'], 
                          capture_output=True, timeout=15)
            print("✓ 已关闭虚拟鼠标显示")
        except Exception as e:
            print(f"⚠️ 关闭虚拟鼠标失败（不影响主要功能）: {str(e)[:80]}")
    
    def connect_device(self):
        """连接设备并启动抖音APP"""
        # 检查Appium服务器是否运行
        try:
            import socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(2)
            result = sock.connect_ex(('localhost', 4723))
            sock.close()
            
            if result != 0:
                print("\n" + "="*60)
                print("❌ 错误：Appium服务器未运行！")
                print("="*60)
                print("\n请按以下步骤启动Appium服务器：")
                print("\n方法1：使用Appium Desktop（推荐）")
                print("  1. 打开 Appium Desktop 应用")
                print("  2. 确保 Host 为 localhost，Port 为 4723")
                print("  3. 点击 'Start Server' 按钮")
                print("  4. 等待服务器启动（看到绿色的日志输出）")
                print("\n方法2：使用命令行")
                print("  在新的终端窗口中运行: appium")
                print("\n启动服务器后，请重新运行本脚本。")
                print("="*60)
                raise ConnectionError("Appium服务器未运行，请先启动Appium服务器")
        except ImportError:
            pass
        
        desired_caps = {
            'platformName': 'Android',
            'platformVersion': self.platform_version,
            'deviceName': self.device_name,
            'appPackage': 'com.ss.android.ugc.aweme',  # 抖音包名
            'appActivity': 'com.ss.android.ugc.aweme.splash.SplashActivity',  # 启动Activity
            'automationName': 'UiAutomator2',
            'noReset': True,  # 不清除应用数据
            'unicodeKeyboard': False,  # 不使用虚拟键盘，保持系统输入法
            'resetKeyboard': False,    # 不重置键盘，避免影响手动输入
            'newCommandTimeout': 6000,
            'ignoreHiddenApiPolicyError': True,  # 忽略隐藏API策略错误
            
            # 关键配置：跳过UiAutomator2的重新安装
            'skipServerInstallation': True,  # 使用已安装的UiAutomator2
            'uiautomator2ServerLaunchTimeout': 60000,  # 增加启动超时时间
        }
        
        print("正在连接设备并启动抖音...")
        
        # 开启虚拟鼠标显示
        self.enable_show_touches()
        
        # 连接到Appium服务器（默认地址）
        self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
        time.sleep(5)  # 等待APP启动
        print("✓ 抖音启动成功！")
        print("📍 提示：手机屏幕上会显示触摸点和轨迹")
        return self.driver
    
    def find_element_safe(self, by, value, timeout=10):
        """
        安全地查找元素
        
        Args:
            by: 定位方式
            value: 定位值
            timeout: 超时时间
            
        Returns:
            找到的元素或None
        """
        try:
            element = WebDriverWait(self.driver, timeout).until(
                EC.presence_of_element_located((by, value))
            )
            return element
        except TimeoutException:
            print(f"未找到元素: {by}={value}")
            return None
    
    def click_element(self, by, value, timeout=10):
        """
        点击指定元素
        
        Args:
            by: 定位方式
            value: 定位值
            timeout: 超时时间
            
        Returns:
            是否点击成功
        """
        element = self.find_element_safe(by, value, timeout)
        if element:
            element.click()
            print(f"成功点击元素: {value}")
            time.sleep(1)
            return True
        return False
    
    def go_to_page(self, page_name):
        """
        前往指定页面
        
        Args:
            page_name: 页面名称 (首页/我/消息/朋友/商城)
        """
        page_map = {
            '首页': '首页',
            '我': '我',
            '消息': '消息',
            '朋友': '朋友',
            '商城': '商城'
        }
        
        if page_name not in page_map:
            print(f"不支持的页面: {page_name}")
            return False
        
        print(f"正在前往 {page_name} 页面...")
        # 通过文本查找底部导航栏按钮
        return self.click_element(AppiumBy.XPATH, 
                                  f'//*[@text="{page_map[page_name]}"]')
    
    def go_to_profile(self):
        """前往个人主页（我的页面）"""
        return self.go_to_page('我')
    
    def go_to_visitor_page(self):
        """前往访客页面（从我的页面进入）"""
        print("正在前往访客页面...")
        
        # 先确保在"我"的页面
        self.go_to_profile()
        time.sleep(1.5)
        
        # 查找并点击访客入口
        # 尝试多种可能的定位方式
        visitor_texts = ['访客', '主页访客', '访客记录']
        
        for text in visitor_texts:
            if self.click_element(AppiumBy.XPATH, f'//*[contains(@text, "{text}")]'):
                time.sleep(1.5)
                return True
        
        print("未找到访客入口")
        return False
    
    def go_to_fans_page(self):
        """前往粉丝页面（从我的页面进入）- 极速版"""
        print("正在前往粉丝页面...")
        
        # 先确保在"我"的页面
        self.go_to_profile()
        time.sleep(0.5)  # 极速优化
        
        # 查找并点击粉丝入口
        fans_texts = ['粉丝', '粉丝数', '我的粉丝']
        
        for text in fans_texts:
            if self.click_element(AppiumBy.XPATH, f'//*[contains(@text, "{text}")]'):
                print("成功进入粉丝页面")
                time.sleep(0.3)  # 极速优化
                return True
        
        print("未找到粉丝入口")
        return False
    
    def go_to_add_friend_page(self):
        """前往添加朋友页面（从我的页面进入）- 极速版"""
        print("正在前往添加朋友页面...")
        
        # 先确保在"我"的页面
        self.go_to_profile()
        time.sleep(0.5)  # 极速优化
        
        # 查找并点击左上角的"添加朋友"按钮
        # 可能的文本：添加朋友、添加、加好友、通讯录
        add_friend_texts = ['添加朋友', '添加', '加好友', '通讯录']
        
        for text in add_friend_texts:
            # 尝试通过文本查找
            if self.click_element(AppiumBy.XPATH, f'//*[contains(@text, "{text}")]'):
                print("✓ 成功点击添加朋友")
                time.sleep(0.5)  # 等待页面加载
                return True
        
        # 如果文本查找失败，尝试通过content-desc查找
        print("尝试通过content-desc查找添加朋友按钮...")
        for text in add_friend_texts:
            if self.click_element(AppiumBy.XPATH, f'//*[contains(@content-desc, "{text}")]'):
                print("✓ 成功点击添加朋友")
                time.sleep(0.5)
                return True
        
        # 最后尝试：直接点击左上角区域的坐标
        print("尝试点击左上角区域...")
        try:
            size = self.driver.get_window_size()
            # 左上角区域：宽度的前1/4，高度的前1/10
            x = int(size['width'] * 0.12)  # 左上角横坐标
            y = int(size['height'] * 0.05)  # 左上角纵坐标
            
            # 使用 adb shell input tap 点击坐标
            import subprocess
            import os
            
            adb_path = 'adb'
            if 'C:\\platform-tools' not in os.environ.get('PATH', ''):
                adb_path = 'C:\\platform-tools\\adb.exe'
            
            result = subprocess.run(
                [adb_path, 'shell', 'input', 'tap', str(x), str(y)],
                capture_output=True,
                timeout=5
            )
            
            if result.returncode == 0:
                print(f"✓ 点击了坐标 ({x}, {y})")
                time.sleep(1)
                return True
            else:
                print(f"⚠️ adb点击失败，返回码: {result.returncode}")
        except Exception as e:
            print(f"点击左上角失败: {str(e)[:100]}")
        
        print("⚠️ 未找到添加朋友入口")
        print("💡 建议：手动点击左上角的添加朋友图标")
        return False
    
    def send_message_to_fans(self, message=None, max_count=None):
        """
        给粉丝发私信（从粉丝页面）
        
        Args:
            message: 要发送的消息内容（如果为None，则从 config.yaml 配置文件读取）
            max_count: 最多发送数量（None表示无限制）
            
        Returns:
            成功发送的数量
        """
        # 如果没有提供消息内容，从配置文件读取
        if message is None:
            message = self.load_private_message_from_config()
        
        if max_count is None:
            print(f"开始给粉丝发私信，直到系统停止...")
        else:
            print(f"开始给粉丝发私信，最多发送 {max_count} 条...")
        
        print(f"💬 消息内容：{message}")
        print("=" * 60)
        
        sent_count = 0
        no_fan_count = 0
        max_no_fan = 3  # 连续未找到粉丝的次数上限
        
        while True:
            try:
                print(f"\n📋 获取当前页面的粉丝列表...")
                
                # 只查找"互相关注"按钮，然后点击按钮左边一点的位置
                fan_click_positions = []
                
                try:
                    # 查找所有的"互相关注"按钮
                    mutual_buttons = self.driver.find_elements(
                        AppiumBy.XPATH,
                        '//*[@text="互相关注"]'
                    )
                    
                    if not mutual_buttons:
                        print(f"  ⚠️ 未找到'互相关注'按钮")
                    else:
                        print(f"  ✓ 找到 {len(mutual_buttons)} 个'互相关注'按钮")
                        
                        # 按Y坐标排序，确保从上到下
                        buttons_with_location = []
                        for btn in mutual_buttons:
                            try:
                                location = btn.location
                                size = btn.size
                                buttons_with_location.append((location['y'], btn, location, size))
                            except:
                                continue
                        
                        # 按Y坐标从上到下排序
                        buttons_with_location.sort(key=lambda x: x[0])
                        
                        # 取前5个按钮，计算点击位置（按钮与屏幕左边缘的正中间）
                        for i, (y, btn, location, size) in enumerate(buttons_with_location[:5]):
                            # 计算点击位置：
                            # "互相关注"按钮X坐标 与 屏幕左边缘(0) 之间的正中间
                            button_x = location['x']
                            click_x = button_x // 2  # 正中间位置
                            click_y = location['y'] + size['height'] // 2  # 按钮中间高度
                            
                            fan_click_positions.append((click_x, click_y, i+1, button_x))
                        
                        print(f"  ✓ 计算出 {len(fan_click_positions)} 个点击位置（按钮与屏幕左边缘的正中间）")
                        for click_x, click_y, row_num, button_x in fan_click_positions:
                            print(f"    第{row_num}行: ({click_x}, {click_y}) [按钮X={button_x}, 中间X={click_x}]")
                    
                except Exception as e:
                    print(f"  ⚠️ 查找'互相关注'按钮失败: {str(e)[:100]}")
                
                if not fan_click_positions:
                    no_fan_count += 1
                    print(f"  ⚠️ 未找到点击位置（{no_fan_count}/{max_no_fan}）")
                    
                    if no_fan_count >= max_no_fan:
                        print("连续多次未找到粉丝，停止发送")
                        break
                    
                    # 滑动加载更多
                    print(f"  🔽 滑动加载更多粉丝...")
                    self.scroll_down(duration=300)
                    time.sleep(1.5)
                    continue
                
                # 重置计数器
                no_fan_count = 0
                
                # 从上到下依次点击每个"互相关注"按钮与屏幕左边缘的正中间位置
                for click_x, click_y, row_num, button_x in fan_click_positions:
                    try:
                        print(f"\n{'='*60}")
                        print(f"👆 点击第 {row_num}/{len(fan_click_positions)} 行")
                        print(f"   按钮X={button_x}, 点击正中间位置: ({click_x}, {click_y})")
                        print(f"{'='*60}")
                        
                        # 使用 adb shell input tap 点击坐标（最稳定的方法）
                        import subprocess
                        import os
                        
                        # 确保 adb 在 PATH 中
                        adb_path = 'adb'
                        if 'C:\\platform-tools' not in os.environ.get('PATH', ''):
                            adb_path = 'C:\\platform-tools\\adb.exe'
                        
                        try:
                            result = subprocess.run(
                                [adb_path, 'shell', 'input', 'tap', str(click_x), str(click_y)],
                                capture_output=True,
                                timeout=5
                            )
                            if result.returncode == 0:
                                print(f"  ✓ 已点击坐标 ({click_x}, {click_y})")
                            else:
                                print(f"  ⚠️ adb点击返回错误码: {result.returncode}")
                                continue
                        except Exception as tap_error:
                            print(f"  ⚠️ adb点击失败: {str(tap_error)[:100]}")
                            continue
                        
                        time.sleep(3)  # 等待进入粉丝主页
                        
                        # 验证是否进入了粉丝主页：查找"发私信"按钮
                        print(f"  🔍 验证是否进入粉丝主页...")
                        
                        # 查找"发私信"按钮（这是粉丝主页的特征元素）
                        message_button = None
                        message_texts = ['发私信', '私信', '发消息', '消息', 'Message']
                        
                        for text in message_texts:
                            try:
                                message_button = self.driver.find_element(
                                    AppiumBy.XPATH,
                                    f'//*[@text="{text}"]'
                                )
                                if message_button:
                                    print(f"  ✓ 已进入粉丝主页，找到按钮: {text}")
                                    break
                            except:
                                continue
                        
                        if not message_button:
                            print(f"  ⚠️ 未找到发私信按钮，返回继续下一个")
                            self.driver.back()
                            time.sleep(1)
                            continue
                        
                        # 点击"发私信"按钮
                        print(f"  📤 点击'发私信'按钮...")
                        message_button.click()
                        time.sleep(2)  # 等待打开私信页面
                        
                        # 查找并点击输入框
                        input_box = None
                        try:
                            input_box = self.driver.find_element(
                                AppiumBy.CLASS_NAME,
                                'android.widget.EditText'
                            )
                        except:
                            try:
                                input_box = self.driver.find_element(
                                    AppiumBy.XPATH,
                                    '//android.widget.EditText'
                                )
                            except:
                                pass
                        
                        if not input_box:
                            print(f"  ⚠️ 未找到输入框，返回继续下一个")
                            self.driver.back()
                            time.sleep(0.5)
                            self.driver.back()
                            time.sleep(0.5)
                            continue
                        
                        # 点击输入框（激活）
                        print(f"  📝 点击输入框...")
                        input_box.click()
                        time.sleep(0.5)
                        
                        # 输入消息
                        print(f"  ⌨️ 输入私信内容...")
                        input_box.clear()
                        input_box.send_keys(message)
                        time.sleep(1)
                        
                        # 查找发送按钮（红色小飞机图标）
                        # 尝试多种方式查找
                        send_button = None
                        
                        # 方法1：通过content-desc查找
                        try:
                            send_button = self.driver.find_element(
                                AppiumBy.XPATH,
                                '//*[contains(@content-desc, "发送") or contains(@content-desc, "send")]'
                            )
                            if send_button:
                                print(f"  ✓ 找到发送按钮（通过content-desc）")
                        except:
                            pass
                        
                        # 方法2：通过文本查找
                        if not send_button:
                            send_texts = ['发送', '送出', 'Send']
                            for text in send_texts:
                                try:
                                    send_button = self.driver.find_element(
                                        AppiumBy.XPATH,
                                        f'//*[@text="{text}"]'
                                    )
                                    if send_button:
                                        print(f"  ✓ 找到发送按钮（通过文本: {text}）")
                                        break
                                except:
                                    continue
                        
                        # 方法3：通过坐标点击（输入框右边的红色小飞机）
                        if not send_button:
                            print(f"  📍 尝试通过坐标点击发送按钮（输入框右边）...")
                            try:
                                # 获取输入框的位置和尺寸
                                input_location = input_box.location
                                input_size = input_box.size
                                
                                # 计算发送按钮的位置（输入框右边）
                                send_x = input_location['x'] + input_size['width'] + 50  # 输入框右边50像素
                                send_y = input_location['y'] + input_size['height'] // 2  # 输入框中间高度
                                
                                # 使用 adb shell input tap 点击
                                import subprocess
                                import os
                                
                                adb_path = 'adb'
                                if 'C:\\platform-tools' not in os.environ.get('PATH', ''):
                                    adb_path = 'C:\\platform-tools\\adb.exe'
                                
                                result = subprocess.run(
                                    [adb_path, 'shell', 'input', 'tap', str(send_x), str(send_y)],
                                    capture_output=True,
                                    timeout=5
                                )
                                
                                if result.returncode == 0:
                                    sent_count += 1
                                    print(f"  ✅ [{sent_count}] 私信已发送（通过坐标点击）")
                                else:
                                    print(f"  ⚠️ adb点击发送按钮失败")
                                
                                time.sleep(0.5)
                            except Exception as coord_error:
                                print(f"  ⚠️ 坐标点击失败: {str(coord_error)[:100]}")
                        else:
                            # 点击找到的发送按钮
                            try:
                                send_button.click()
                                sent_count += 1
                                print(f"  ✅ [{sent_count}] 私信已发送")
                                time.sleep(0.5)
                            except Exception as click_error:
                                print(f"  ⚠️ 点击发送按钮失败: {str(click_error)[:100]}")
                        
                        # 返回粉丝列表（多次尝试确保返回成功）
                        print(f"  ↩️ 开始返回粉丝列表...")
                        
                        # 第1次返回：从私信页面返回到粉丝主页
                        self.driver.back()
                        time.sleep(1)
                        
                        # 第2次返回：从粉丝主页返回到粉丝列表
                        self.driver.back()
                        time.sleep(1)
                        
                        # 验证是否回到了粉丝列表（查找"互相关注"按钮）
                        max_return_attempts = 3
                        for attempt in range(max_return_attempts):
                            try:
                                # 检查是否能找到"互相关注"按钮（说明回到了粉丝列表）
                                check_buttons = self.driver.find_elements(
                                    AppiumBy.XPATH,
                                    '//*[@text="互相关注"]'
                                )
                                if check_buttons:
                                    print(f"  ✓ 已成功返回粉丝列表（找到 {len(check_buttons)} 个'互相关注'按钮）")
                                    break
                                else:
                                    # 没找到，可能还没回到粉丝列表，再返回一次
                                    print(f"  ⚠️ 未找到'互相关注'按钮，尝试再次返回 ({attempt+1}/{max_return_attempts})")
                                    self.driver.back()
                                    time.sleep(1)
                            except:
                                # 查找失败，尝试再返回
                                if attempt < max_return_attempts - 1:
                                    self.driver.back()
                                    time.sleep(1)
                        
                        # 检查是否达到上限
                        if max_count and sent_count >= max_count:
                            print(f"\n✅ 已达到设定的数量上限: {max_count}")
                            return sent_count
                    
                    except Exception as fan_error:
                        print(f"  ⚠️ 处理粉丝 #{row_num} 时出错: {str(fan_error)[:100]}")
                        # 尝试返回
                        try:
                            self.driver.back()
                            time.sleep(0.5)
                            self.driver.back()
                            time.sleep(0.5)
                        except:
                            pass
                        continue
                
                # 本页粉丝处理完毕，滑动到下一页
                print(f"\n📄 本页 {len(fan_click_positions)} 个点击位置处理完毕")
                print(f"🔽 滑动加载下一页粉丝...")
                self.scroll_down(duration=300)
                time.sleep(1.5)
                
            except Exception as e:
                error_msg = str(e)
                print(f"\n⚠️ 页面处理出错: {error_msg[:150]}")
                # 尝试返回粉丝列表
                try:
                    self.driver.back()
                    time.sleep(0.5)
                except:
                    pass
                # 滑动到下一页继续
                self.scroll_down(duration=300)
                time.sleep(1)
                continue
        
        print(f"\n✅ 完成！共成功发送了 {sent_count} 条私信")
        return sent_count
    
    def click_all_follow_buttons_in_order(self, max_count=None):
        """
        从上到下依次点击所有"关注"和"回关"按钮（每页全部点完再滑动）
        
        Args:
            max_count: 最多点击数量（None表示无限制，直到系统停止）
            
        Returns:
            成功点击的数量
        """
        if max_count is None:
            print("开始从上到下点击【所有关注和回关】按钮，直到系统停止...")
        else:
            print(f"开始从上到下点击【所有关注和回关】按钮，最多点击 {max_count} 个...")
        
        print("💡 策略：每一页全部点完后再滑动翻页")
        
        clicked_count = 0
        no_button_count = 0  # 连续未找到按钮的次数
        max_no_button = 3  # 连续3次未找到按钮就停止
        need_refresh = False  # 是否需要刷新页面的标志
        
        while True:
            # 检查是否需要刷新页面
            if need_refresh:
                need_refresh = False
                continue  # 直接继续下一轮循环，重新查找按钮
            try:
                # 查找当前页面所有的"关注"和"回关"按钮
                follow_buttons = self.driver.find_elements(
                    AppiumBy.XPATH,
                    '//*[@text="关注" or @text="回关"]'
                )
                
                if not follow_buttons:
                    no_button_count += 1
                    print(f"未找到按钮（{no_button_count}/{max_no_button}）")
                    
                    # 检查是否出现"没有更多"提示
                    try:
                        no_more_texts = ['没有更多', '暂无更多', '到底了', '已经到底了', '没有了']
                        for text in no_more_texts:
                            no_more_elem = self.driver.find_elements(
                                AppiumBy.XPATH,
                                f'//*[contains(@text, "{text}")]'
                            )
                            if no_more_elem:
                                print(f"\n📍 检测到'{text}'提示，准备刷新列表...")
                                print("  ↩️ 退出添加朋友页面...")
                                self.driver.back()
                                time.sleep(1.5)
                                
                                print("  🔄 重新进入添加朋友页面...")
                                if self.go_to_add_friend_page():
                                    print("  ✓ 已重新进入，继续操作...")
                                    no_button_count = 0  # 重置计数器
                                    time.sleep(1.5)
                                    need_refresh = True  # 设置刷新标志
                                    break  # 跳出for循环
                                else:
                                    print("  ⚠️ 重新进入失败")
                                    return clicked_count
                    except Exception as e:
                        print(f"检查'没有更多'提示时出错: {str(e)[:100]}")
                    
                    # 如果设置了刷新标志，跳过后续逻辑
                    if need_refresh:
                        continue
                    
                    if no_button_count >= max_no_button:
                        print("连续多次未找到按钮，可能已到达列表底部")
                        break
                    
                    # 尝试滑动加载更多
                    self.scroll_down()
                    time.sleep(0.8)
                    continue
                
                # 重置计数器
                no_button_count = 0
                
                # 统计当前页面找到的按钮数量
                page_button_count = len(follow_buttons)
                print(f"\n📍 当前页面找到 {page_button_count} 个按钮，开始依次点击...")
                
                # 当前页面已点击的数量
                page_clicked_count = 0
                
                # 依次点击当前页面的所有按钮
                for button in follow_buttons:
                    try:
                        # 重新检查按钮是否仍然可见可点击（因为页面可能有变化）
                        if not button.is_displayed() or not button.is_enabled():
                            print(f"⚠️ 按钮已不可见或不可点击，跳过")
                            continue
                        
                        # 获取按钮类型（关注或回关）
                        button_type = button.text if button.text in ['关注', '回关'] else '关注/回关'
                        
                        # 先获取用户名（改进的方法）
                        username = self._get_username_for_button(button)
                        
                        # 立即点击按钮
                        button.click()
                        clicked_count += 1
                        page_clicked_count += 1
                        
                        # 打印操作信息（显示按钮类型和用户名）
                        if username:
                            print(f"  ✓ [{clicked_count}] 已{button_type}: {username}")
                        else:
                            print(f"  ✓ [{clicked_count}] 已{button_type}: (用户名获取失败)")
                        
                        time.sleep(0.5)  # 等待按钮点击效果
                        
                        # 检查是否不小心跳转到了用户主页（某些情况下点击会跳转）
                        try:
                            # 如果能找到"发私信"按钮，说明跳转到了用户主页
                            private_msg_btn = self.driver.find_elements(
                                AppiumBy.XPATH,
                                '//*[@text="发私信" or @text="私信"]'
                            )
                            if private_msg_btn:
                                print(f"    ⚠️ 检测到跳转到用户主页，正在返回...")
                                self.driver.back()
                                time.sleep(0.8)
                        except:
                            pass
                        
                        # 检查是否达到上限
                        if max_count and clicked_count >= max_count:
                            print(f"\n已达到设定的数量上限: {max_count}")
                            return clicked_count
                        
                    except Exception as e:
                        error_msg = str(e)
                        if "已关注" in error_msg or "reach" in error_msg.lower() or "limit" in error_msg.lower():
                            print(f"\n⚠️  系统提示: 可能已达到关注上限")
                            print(f"完成！共成功操作了 {clicked_count} 个用户")
                            return clicked_count
                        elif "stale" in error_msg.lower():
                            print(f"  ⚠️ 元素已失效，跳过")
                            continue
                        else:
                            print(f"  ⚠️ 点击按钮时出错: {e}")
                            continue
                
                # 当前页面点击完成
                print(f"📄 当前页面完成！已点击 {page_clicked_count}/{page_button_count} 个按钮")
                
                if page_clicked_count == 0:
                    print("本页没有成功点击任何按钮，可能已经全部操作过")
                
                # 向下滑动到下一页
                print("🔽 向下滑动加载下一页...")
                self.scroll_down(duration=200)
                time.sleep(0.5)  # 等待页面加载
                
                # 滑动后检查是否出现"没有更多"提示
                try:
                    no_more_texts = ['没有更多', '暂无更多', '到底了', '已经到底了', '没有了']
                    for text in no_more_texts:
                        no_more_elem = self.driver.find_elements(
                            AppiumBy.XPATH,
                            f'//*[contains(@text, "{text}")]'
                        )
                        if no_more_elem:
                            print(f"\n📍 检测到'{text}'提示，准备刷新列表...")
                            print("  ↩️ 退出添加朋友页面...")
                            self.driver.back()
                            time.sleep(1.5)
                            
                            print("  🔄 重新进入添加朋友页面...")
                            if self.go_to_add_friend_page():
                                print("  ✓ 已重新进入，继续操作...")
                                time.sleep(1.5)
                                need_refresh = True  # 设置刷新标志
                                break  # 跳出for循环，返回外层while循环
                            else:
                                print("  ⚠️ 重新进入失败")
                                return clicked_count
                except Exception as e:
                    print(f"检查'没有更多'提示时出错: {str(e)[:100]}")
                
            except Exception as e:
                print(f"查找按钮时出错: {e}")
                break
        
        print(f"\n✅ 完成！共成功操作了 {clicked_count} 个用户")
        return clicked_count
    
    def click_follow_buttons_in_order(self, max_count=None, button_text="关注"):
        """
        从上到下依次点击关注/回关按钮，直到无法继续点击（优化版）
        
        Args:
            max_count: 最多点击数量（None表示无限制，直到系统停止）
            button_text: 要点击的按钮文本（"关注"或"回关"）
            
        Returns:
            成功点击的数量
        """
        if max_count is None:
            print(f"开始从上到下点击【{button_text}】按钮，直到系统停止...")
        else:
            print(f"开始从上到下点击【{button_text}】按钮，最多点击 {max_count} 个...")
        
        clicked_count = 0
        no_button_count = 0  # 连续未找到按钮的次数
        max_no_button = 3  # 连续3次未找到按钮就停止
        
        while True:
            try:
                # 极速查找按钮（最简化，只查找text属性）
                follow_buttons = self.driver.find_elements(
                    AppiumBy.XPATH,
                    f'//*[@text="{button_text}"]'
                )
                
                if not follow_buttons:
                    no_button_count += 1
                    print(f"未找到{button_text}按钮（{no_button_count}/{max_no_button}）")
                    
                    if no_button_count >= max_no_button:
                        print(f"连续多次未找到{button_text}按钮，可能已到达列表底部")
                        break
                    
                    # 尝试滑动加载更多（优化：减少等待时间）
                    self.scroll_down()
                    time.sleep(0.8)
                    continue
                
                # 重置计数器
                no_button_count = 0
                button_clicked_this_round = False
                
                # 极速点击（先获取用户名，再快速点击）
                for button in follow_buttons:
                    try:
                        if button.is_displayed() and button.is_enabled():
                            # 使用改进的用户名获取方法
                            username = self._get_username_for_button(button)
                            
                            # 立即点击按钮（不等待）
                            button.click()
                            clicked_count += 1
                            button_clicked_this_round = True
                            
                            # 打印操作信息（显示用户名）
                            if username:
                                print(f"✓ [{clicked_count}] 已{button_text}: {username}")
                            else:
                                print(f"✓ [{clicked_count}] 已{button_text}: (用户名获取失败)")
                            
                            time.sleep(0.2)  # 极速间隔
                            
                            # 检查是否达到上限
                            if max_count and clicked_count >= max_count:
                                print(f"已达到设定的数量上限: {max_count}")
                                return clicked_count
                            
                            break  # 只点击一个，然后滑动
                            
                    except Exception as e:
                        error_msg = str(e)
                        if "已关注" in error_msg or "reach" in error_msg.lower() or "limit" in error_msg.lower():
                            print(f"⚠️  系统提示: 可能已达到关注上限")
                            print(f"完成！共成功关注了 {clicked_count} 个用户")
                            return clicked_count
                        else:
                            print(f"点击按钮时出错: {e}")
                            continue
                
                if not button_clicked_this_round:
                    print("本轮未能点击任何按钮，尝试滑动...")
                
                # 向下滑动以加载更多（极速版）
                self.scroll_down(duration=200)  # 更快的滑动（0.2秒）
                time.sleep(0.3)  # 进一步优化到0.3秒
                
            except Exception as e:
                print(f"查找关注按钮时出错: {e}")
                break
        
        print(f"完成！共成功关注了 {clicked_count} 个用户")
        return clicked_count
    
    def _get_username_for_button(self, button):
        """
        获取按钮对应的用户名（改进版，支持多种策略）
        
        Args:
            button: 关注/回关按钮元素
            
        Returns:
            用户名字符串，如果获取失败返回空字符串
        """
        try:
            # 获取按钮的位置信息
            button_location = button.location
            button_size = button.size
            button_x = button_location['x']
            button_y = button_location['y']
            button_center_y = button_y + button_size['height'] // 2
            
            # 策略1：查找按钮左边、同一行的所有TextView（最可靠）
            # 找到所有的TextView元素
            all_text_views = self.driver.find_elements(AppiumBy.CLASS_NAME, 'android.widget.TextView')
            
            # 过滤规则
            exclude_texts = {'关注', '回关', '粉丝', '获赞', '作品', '喜欢', '赞', '抖音号', ''}
            
            # 候选用户名列表（按距离排序）
            candidates = []
            
            for text_elem in all_text_views:
                try:
                    text = text_elem.text
                    
                    # 跳过空文本和排除的文本
                    if not text or text in exclude_texts:
                        continue
                    
                    # 跳过包含数字的文本（可能是粉丝数、获赞数等）
                    if text.isdigit():
                        continue
                    
                    # 跳过明显是数字统计的文本（如 "1.2万"、"123"等）
                    if any(char in text for char in ['万', '亿', 'w', 'W']) and any(char.isdigit() for char in text):
                        continue
                    
                    # 获取文本元素的位置
                    text_location = text_elem.location
                    text_size = text_elem.size
                    text_x = text_location['x']
                    text_y = text_location['y']
                    text_center_y = text_y + text_size['height'] // 2
                    
                    # 判断是否在按钮左边，并且在合理的垂直范围内（同一行或接近）
                    if text_x < button_x:
                        # 计算垂直距离
                        y_distance = abs(text_center_y - button_center_y)
                        
                        # 在同一行或很近的范围内（允许150像素的偏差，适应不同布局）
                        if y_distance < 150:
                            # 计算水平距离
                            x_distance = button_x - (text_x + text_size['width'])
                            
                            # 保存候选项：(垂直距离, 水平距离, 文本)
                            candidates.append((y_distance, x_distance, text))
                
                except Exception:
                    continue
            
            # 按优先级排序：垂直距离最近的优先，然后是水平距离最近的
            if candidates:
                candidates.sort(key=lambda x: (x[0], x[1]))
                # 返回最匹配的用户名
                username = candidates[0][2]
                
                # 进一步验证：用户名应该是合理的长度（通常不会太短或太长）
                if 1 <= len(username) <= 30:
                    return username
            
            # 如果上面的方法都失败了，返回空字符串
            return ""
            
        except Exception:
            # 静默失败，不打印错误信息
            return ""
    
    def _get_username_simple(self, button):
        """
        极速获取用户名（最简化，不影响速度）
        
        Args:
            button: 按钮元素
            
        Returns:
            用户名字符串
        """
        try:
            # 只尝试最快的一种方法，立即返回
            parent = button.find_element(AppiumBy.XPATH, '..')
            texts = parent.find_elements(AppiumBy.CLASS_NAME, 'android.widget.TextView')
            
            # 只检查前2个元素
            for text_elem in texts[:2]:
                text = text_elem.text
                if text and text not in ['关注', '回关', '']:
                    return text
            
            return ""  # 快速返回空字符串
        except:
            return ""  # 立即返回，不浪费时间
    
    def _get_username_near_button_fast(self, button):
        """
        快速获取按钮附近的用户名（优化版）
        
        Args:
            button: 关注/回关按钮元素
            
        Returns:
            用户名字符串
        """
        try:
            # 快速方法：直接从父元素查找
            parent = button.find_element(AppiumBy.XPATH, '..')
            
            # 尝试查找昵称相关的元素
            try:
                username_elem = parent.find_element(AppiumBy.XPATH, './/*[contains(@resource-id, "title") or contains(@resource-id, "name")]')
                username = username_elem.text
                if username and username not in ['关注', '回关', '']:
                    return username
            except:
                pass
            
            # 备用方案：查找父元素中的第一个TextView
            try:
                texts = parent.find_elements(AppiumBy.CLASS_NAME, 'android.widget.TextView')
                for text_elem in texts[:3]:  # 只检查前3个
                    text = text_elem.text
                    if text and text not in ['关注', '回关', '粉丝', '获赞', '']:
                        return text
            except:
                pass
            
            return "用户"
            
        except:
            return "用户"
    
    def _get_username_near_button(self, button):
        """
        获取关注按钮附近的用户名（兼容旧方法）
        
        Args:
            button: 关注按钮元素
            
        Returns:
            用户名字符串
        """
        return self._get_username_near_button_fast(button)
    
    def scroll_down(self, duration=500):
        """
        向下滑动屏幕
        
        Args:
            duration: 滑动持续时间(毫秒)
        """
        size = self.driver.get_window_size()
        start_x = size['width'] // 2
        start_y = size['height'] * 0.8
        end_y = size['height'] * 0.2
        
        self.driver.swipe(start_x, start_y, start_x, end_y, duration)
    
    def scroll_up(self, duration=500):
        """
        向上滑动屏幕
        
        Args:
            duration: 滑动持续时间(毫秒)
        """
        size = self.driver.get_window_size()
        start_x = size['width'] // 2
        start_y = size['height'] * 0.2
        end_y = size['height'] * 0.8
        
        self.driver.swipe(start_x, start_y, start_x, end_y, duration)
    
    def click_by_text(self, text, partial=False):
        """
        通过文本点击元素
        
        Args:
            text: 要查找的文本
            partial: 是否部分匹配
            
        Returns:
            是否点击成功
        """
        if partial:
            xpath = f'//*[contains(@text, "{text}")]'
        else:
            xpath = f'//*[@text="{text}"]'
        
        return self.click_element(AppiumBy.XPATH, xpath)
    
    def click_by_id(self, resource_id):
        """
        通过资源ID点击元素
        
        Args:
            resource_id: 资源ID
            
        Returns:
            是否点击成功
        """
        return self.click_element(AppiumBy.ID, resource_id)
    
    def get_current_activity(self):
        """获取当前Activity"""
        if self.driver:
            return self.driver.current_activity
        return None
    
    def take_screenshot(self, filename='screenshot.png'):
        """
        截图
        
        Args:
            filename: 保存的文件名
        """
        if self.driver:
            self.driver.save_screenshot(filename)
            print(f"截图已保存: {filename}")
    
    def close(self):
        """关闭连接"""
        if self.driver:
            print("正在关闭连接...")
            self.driver.quit()
            # 关闭虚拟鼠标显示
            self.disable_show_touches()
            print("连接已关闭")


def example_visitor_auto_follow():
    """
    示例：访客页面自动关注
    进入主页访客页面，从上到下点击红色关注按钮，直到系统停止
    """
    automation = DouyinAutomation()
    
    try:
        # 1. 连接设备并启动抖音
        automation.connect_device()
        
        # 2. 前往访客页面
        automation.go_to_visitor_page()
        
        # 3. 从上到下依次点击关注按钮（无上限，直到系统停止）
        automation.click_follow_buttons_in_order(max_count=None, button_text="关注")
        
        # 4. 截图保存
        automation.take_screenshot('visitor_follow_result.png')
        
    except Exception as e:
        print(f"执行过程中出错: {e}")
    finally:
        # 5. 关闭连接
        time.sleep(2)
        automation.close()


def example_fans_follow_back():
    """
    示例：粉丝页面批量回关（优化版）
    进入"我"→"粉丝"页面，从上到下点击"回关"按钮，直到系统停止
    """
    automation = DouyinAutomation()
    
    try:
        # 1. 连接设备并启动抖音
        automation.connect_device()
        
        # 2. 前往粉丝页面
        automation.go_to_fans_page()
        
        # 3. 从上到下依次点击回关按钮（无上限，直到系统停止）
        print("\n开始批量回关操作...")
        print("=" * 60)
        automation.click_follow_buttons_in_order(max_count=None, button_text="回关")
        
        # 4. 截图保存
        automation.take_screenshot('fans_follow_back_result.png')
        
    except Exception as e:
        print(f"执行过程中出错: {e}")
    finally:
        # 5. 关闭连接
        time.sleep(1)
        automation.close()


def example_add_friend_follow():
    """
    示例：添加朋友页面批量关注/回关（优化版）
    进入"我"→"添加朋友"页面，从上到下点击所有"关注"和"回关"按钮
    """
    automation = DouyinAutomation()
    
    try:
        # 1. 连接设备并启动抖音
        automation.connect_device()
        
        # 2. 前往添加朋友页面
        success = automation.go_to_add_friend_page()
        
        if not success:
            print("\n⚠️ 自动导航失败，请手动操作：")
            print("  1. 点击底部的【我】")
            print("  2. 点击左上角的【添加朋友】图标")
            print("  3. 确认进入添加朋友页面后")
            input("按回车键继续...")
        
        # 3. 从上到下依次点击所有按钮（关注和回关都点击）
        print("\n开始在添加朋友页面批量操作...")
        print("=" * 60)
        print("💡 将自动点击所有'关注'和'回关'按钮")
        print("=" * 60)
        automation.click_all_follow_buttons_in_order(max_count=None)
        
        # 4. 截图保存
        automation.take_screenshot('add_friend_result.png')
        
    except Exception as e:
        print(f"执行过程中出错: {e}")
    finally:
        # 5. 关闭连接
        time.sleep(1)
        automation.close()


def example_send_messages_to_fans():
    """
    示例：给粉丝发私信感谢
    进入粉丝页面，给每个粉丝发私信：感谢关注，祝愿生活越来越美好
    """
    automation = DouyinAutomation()
    
    try:
        # 1. 连接设备并启动抖音
        automation.connect_device()
        
        # 2. 前往粉丝页面
        automation.go_to_fans_page()
        
        # 3. 提示信息
        print("\n" + "=" * 60)
        print("📝 私信内容将从 config.yaml 配置文件读取")
        print("💡 如需修改私信内容，请编辑 config.yaml 文件中的 private_message.content")
        print("=" * 60)
        print("\n⚠️ 注意：将无限制发送私信，直到所有粉丝发送完毕")
        print("=" * 60)
        
        # 4. 开始发送私信（无数量限制，message=None 会自动从文件读取）
        print("\n开始给粉丝发私信...")
        print("=" * 60)
        automation.send_message_to_fans(message=None, max_count=None)
        
        # 6. 截图保存
        automation.take_screenshot('send_messages_result.png')
        
    except Exception as e:
        print(f"执行过程中出错: {e}")
    finally:
        # 7. 关闭连接
        time.sleep(1)
        automation.close()


def example_custom_automation():
    """
    示例：自定义自动化流程
    演示如何实现自定义的页面跳转和按钮点击
    """
    automation = DouyinAutomation()
    
    try:
        # 连接设备
        automation.connect_device()
        
        # 示例1: 前往首页
        automation.go_to_page('首页')
        time.sleep(2)
        
        # 示例2: 通过文本点击按钮
        automation.click_by_text('推荐')
        time.sleep(1)
        
        # 示例3: 滑动操作
        automation.scroll_down()
        time.sleep(1)
        
        # 示例4: 前往我的页面
        automation.go_to_profile()
        time.sleep(2)
        
        # 示例5: 点击特定元素
        automation.click_by_text('获赞', partial=True)
        
    except Exception as e:
        print(f"执行过程中出错: {e}")
    finally:
        automation.close()


if __name__ == '__main__':
    print("=" * 60)
    print("抖音自动化操作脚本（优化版 v2.2）")
    print("=" * 60)
    print("\n请选择要执行的操作:")
    print("1. 访客页面自动关注")
    print("2. 粉丝页面批量回关 ⭐推荐")
    print("3. 添加朋友页面自动批量操作")
    print("   → 自动点击所有'关注'和'回关'按钮（每页点完再翻页）")
    print("4. 给粉丝发私信感谢 💬🆕")
    print("   → 叮咚！一份专属感谢已送达～")
    print("5. 自定义自动化流程示例")
    print("=" * 60)
    print("\n💡 提示:")
    print("  - 会自动开启虚拟鼠标，能看到手机屏幕上的操作")
    print("  - 每次点击都会打印用户名和按钮类型（多重方法获取）")
    print("  - 无数量限制，直到系统停止")
    print("  - 速度已优化，更快执行")
    print("  - 已关闭虚拟键盘，不影响手动输入")
    print("  - 建议先关闭手机动画（运行: .\\关闭手机动画.bat）")
    print("=" * 60)
    
    choice = input("\n请输入选项(1/2/3/4/5): ").strip()
    
    if choice == '1':
        example_visitor_auto_follow()
    elif choice == '2':
        example_fans_follow_back()
    elif choice == '3':
        example_add_friend_follow()
    elif choice == '4':
        example_send_messages_to_fans()
    elif choice == '5':
        example_custom_automation()
    else:
        print("无效的选项！")

        print("请手动打开手机上的抖音app，然后重新运行本脚本进行自动化操作。")
